/**
 * Copyright 2011 Honeywell Federal Manufacturing & Technologies, LLC. This material was produced under U.S. Government
 * contract DE-ACO4-01AL66850, Honeywell Federal Manufacturing & Technologies, LLC, (FM&T) with the U. S. Department of
 * Energy for the operation of the Kansas City Plant . The U.S. Government has rights to use, reproduce, and distribute
 * this software.  NEITHER THE GOVERNMENT NOR Honeywell Federal Manufacturing & Technologies, LLC, MAKES ANY WARRANTY,
 * EXPRESS OR IMPLIED, OR ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE.  If software is modified to produce
 * derivative works, such modified software should be clearly marked, so as not to confuse it with the version available
 * from FM&T.
 *
 * Additionally, this program is free software; you can redistribute it and/or modify it under the terms of the GNU
 * General Public License Version 3 published by the Free Software Foundation. Accordingly, this program is distributed in
 * the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this library.
 * If not, see http://www.gnu.org/licenses/.
 */
package org.braintrain.tapestry.view.util;

import org.apache.hivemind.util.Defense;
import org.apache.tapestry.engine.ServiceEncoding;
import org.apache.tapestry.record.PropertyChange;
import org.apache.tapestry.record.PropertyPersistenceStrategy;
import org.apache.tapestry.record.RecordUtils;
import org.apache.tapestry.record.WebSessionAttributeCallback;
import org.apache.tapestry.web.WebRequest;
import org.apache.tapestry.web.WebSession;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

/**
 * Derivative of SessionPropertyPersistenceScope
 * - only stores properties while the given page is the most recently rendered page.
 */
public class SessionPagePropertyPersistenceStrategy implements PropertyPersistenceStrategy {

    /**
     * unique persistence name
     */
    public static final String      STRATEGY_ID     = "session:page";

    /**
     * Session key for the last rendered page name
     */
    public static final String LAST_RENDERED_PAGE_ID = "lastPage";

    // Really, the name of the servlet; used as a prefix on all HttpSessionAttribute keys
    // to keep things straight if multiple Tapestry apps are deployed
    // in the same WAR.

    private String                          _applicationId;
    private WebRequest                      _request;

    public void store(String pageName, String idPath, String propertyName, Object newValue) {
            Defense.notNull(pageName, "pageName");
            Defense.notNull(propertyName, "propertyName");

            WebSession session = _request.getSession(true);

            String attributeName = RecordUtils.buildChangeKey(STRATEGY_ID, _applicationId, pageName, idPath, propertyName);

            session.setAttribute(attributeName, newValue);
    }

    public Collection getStoredChanges(String pageName) {
            Defense.notNull(pageName, "pageName");

            WebSession session = _request.getSession(false);

            if (session == null) return Collections.EMPTY_LIST;

            /*
             * Single largest difference between this and Session persistence:
             * If the last rendered page is not this page (or is null) then don't return any
             * property values, and set any values found to null.
             */
            String lastPage = (String) session.getAttribute(getLastPageKey(_applicationId));
            if (lastPage == null || ! lastPage.equals(pageName)) {
                    // discard values and return empty list
                    discardStoredChanges(pageName);
                    return Collections.EMPTY_LIST;
            }

            final Collection result = new ArrayList();

            WebSessionAttributeCallback callback = new WebSessionAttributeCallback() {
                    @SuppressWarnings("unchecked")
                    public void handleAttribute(WebSession ws, String name) {
                            PropertyChange change = RecordUtils.buildChange(name, ws.getAttribute(name));
                            result.add(change);
                    }
            };
            RecordUtils.iterateOverMatchingAttributes(STRATEGY_ID, _applicationId, pageName, session, callback);
            return result;
    }

    public void discardStoredChanges(String pageName) {
            WebSession session = _request.getSession(false);
            if (session == null) return;
            WebSessionAttributeCallback callback = new WebSessionAttributeCallback() {
                    public void handleAttribute(WebSession ws, String name) {
                            ws.setAttribute(name, null);
                    }
            };
            RecordUtils.iterateOverMatchingAttributes(STRATEGY_ID, _applicationId, pageName, session, callback);
    }

    public void addParametersForPersistentProperties(ServiceEncoding encoding, boolean post) {
            // nothing to do - we don't use query parameters for sessions
    }

    /**
     * @param applicationName (injected by HiveMind) for uniqueness of session attribute names
     */
    public void setApplicationId(String applicationName) {
            _applicationId = applicationName;
    }

    /**
     * @param request (injected by HiveMind)
     */
    public void setRequest(WebRequest request) {
            _request = request;
    }

    /**
     * @param appId
     * @return application specific session key name for the last rendered page
     * static so the PageEndRenderListener can also use it - there may be a better way for this
     */
    public static String getLastPageKey(String appId) {
            return new StringBuilder().append(STRATEGY_ID).append(",").append(appId).append(",").toString();
    }
}